﻿using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using System;
using System.Linq;

namespace Lightray.Hattrick.ChppLib.EntityTypes
{
    [ComVisible(true)]
    public class TeamLeagueStats
    {
        #region Properties
        public DateTime DataTime { get; private set; }
        public int HomeWins { get; private set; }
        public int HomeDraws { get; private set; }
        public int HomeLosses { get; private set; }
        public int HomeGoalsFor { get; private set; }
        public int HomeGoalsAgainst { get; private set; }
        public int AwayWins { get; private set; }
        public int AwayDraws { get; private set; }
        public int AwayLosses { get; private set; }
        public int AwayGoalsFor { get; private set; }
        public int AwayGoalsAgainst { get; private set; }        
        public int PlayedMatches { get; private set; }
        public int Points { get; private set; }
        public int Wins { get; private set; }
        public int Draws { get; private set; }
        public int Losses { get; private set; }
        public int GoalsFor { get; private set; }
        public int GoalsAgainst { get; private set; }
        public int Position { get; internal set; }        

        internal int TeamID { get; private set; }
        internal int LeagueLevelUnitId { get; private set; }
        #endregion

        protected TeamLeagueStats() 
        {
            HomeWins = 0;
            HomeDraws = 0;
            HomeLosses = 0;
            HomeGoalsFor = 0;
            HomeGoalsAgainst = 0;
            AwayWins = 0;
            AwayDraws = 0;
            AwayLosses = 0;
            AwayGoalsFor = 0;
            AwayGoalsAgainst = 0;
            Position = -1;
        }

        public static IEnumerable<TeamLeagueStats> FromLeagueDeatilsXml(XDocument leagueDetailsXml)
        {
            HattrickXmlReader reader = new HattrickXmlReader(leagueDetailsXml);
            DateTime dataTime = reader.ReadDate("FetchedDate");
            int leagueLevelUnitId = reader.ReadNumber("LeagueLevelUnitID");

            List<TeamLeagueStats> ret = new List<TeamLeagueStats>();
            foreach (var teamNode in leagueDetailsXml.Descendants("Team"))
                ret.Add(TeamLeagueStats.FromLeagueDetailsNode(teamNode, leagueLevelUnitId, dataTime));

            return ret;
        }

        private static TeamLeagueStats FromLeagueDetailsNode(XElement teamNode, int leagueLevelUnitId, DateTime dataTime)
        {
            HattrickXmlReader reader = new HattrickXmlReader(teamNode);
            return new TeamLeagueStats()
            {
                DataTime = dataTime,
                LeagueLevelUnitId = leagueLevelUnitId,
                TeamID = reader.ReadNumber("TeamID"),
                Position = reader.ReadNumber("Position"),
                PlayedMatches = reader.ReadNumber("Matches"),
                GoalsFor = reader.ReadNumber("GoalsFor"),
                GoalsAgainst = reader.ReadNumber("GoalsAgainst"),
                Points = reader.ReadNumber("Points"),
            };
        }

        protected TeamLeagueStats(int teamId, int leagueLevelUnitId, DateTime dataTime)
            : this()
        {
            TeamID = teamId;
            LeagueLevelUnitId = leagueLevelUnitId;
            DataTime = dataTime;
        }

        public static IEnumerable<TeamLeagueStats> FromLeagueFixturesXml(XDocument leagueFixturesXml)
        {
            if (leagueFixturesXml.Descendants("HomeTeamID").Count() == 0)
                return null;

            HattrickXmlReader reader = new HattrickXmlReader(leagueFixturesXml);
            int leagueLevelUnitId = reader.ReadNumber("LeagueLevelUnitID");
            DateTime dataTime = reader.ReadDate("FetchedDate");

            List<TeamLeagueStats> ret = new List<TeamLeagueStats>();

            var leagueTeamIds = (from teamId in leagueFixturesXml.Descendants("HomeTeamID")
                                 select teamId.Value).Distinct();
            foreach (var teamId in leagueTeamIds)
            {
                var teamMatches = from match in leagueFixturesXml.Descendants("Match")
                                  where match.Element("HomeTeam").Element("HomeTeamID").Value == teamId ||
                                        match.Element("AwayTeam").Element("AwayTeamID").Value == teamId
                                  select match;

                ret.Add(TeamLeagueStats.FromLeagueFixturesNode(teamMatches, Convert.ToInt32(teamId), leagueLevelUnitId, dataTime));
            }

            // calculate league position
            var leagueTeamStats = from teamStats in ret
                                  orderby
                                    teamStats.Points descending,
                                    (teamStats.GoalsFor - teamStats.GoalsAgainst) descending,
                                    teamStats.GoalsFor descending
                                  select teamStats;

            for (int i = 0; i < leagueTeamStats.Count(); i++)
                leagueTeamStats.ElementAt(i).Position = i + 1;

            return ret;
        }

        private static TeamLeagueStats FromLeagueFixturesNode(IEnumerable<XElement> matchNodes, int teamId, int leagueLevelUnitId, DateTime dataTime)
        {
            TeamLeagueStats stats = new TeamLeagueStats(teamId, leagueLevelUnitId, dataTime);
            foreach (var match in matchNodes)
            {
                if (match.Element("HomeGoals") != null)
                    stats.addMatch(match);
            }

            if (stats.Position < 0)
                stats.Position = 0;

            return stats;
        }

        private void addMatch(XElement matchNode)
        {
            HattrickXmlReader reader = new HattrickXmlReader(matchNode);

            LeagueMatchLocation location = getMatchLocation(reader);
            int homeScore = reader.ReadNumber("HomeGoals");
            int awayScore = reader.ReadNumber("AwayGoals");

            int selfGoals, oppositionGoals;
            addGoalStats(location, homeScore, awayScore, out selfGoals, out oppositionGoals);

            LeagueMatchResult result = determineResult(selfGoals, oppositionGoals, location);
            addResultStats(result);

            calculateCombinedStats();
        }

        private void calculateCombinedStats()
        {
            GoalsFor = HomeGoalsFor + AwayGoalsFor;
            GoalsAgainst = HomeGoalsAgainst + AwayGoalsAgainst;
            Wins = HomeWins + AwayWins;
            Draws = HomeDraws + AwayDraws;
            Losses = HomeLosses + AwayLosses;
            PlayedMatches = Wins + Draws + Losses;
            Points = 3 * Wins + Draws;
        }

        private void addGoalStats(LeagueMatchLocation location, int homeScore, int awayScore, out int selfGoals, out int oppositionGoals)
        {
            selfGoals = 0;
            oppositionGoals = 0;

            switch (location)
            {
                case LeagueMatchLocation.Home:
                    selfGoals = homeScore;
                    oppositionGoals = awayScore;

                    HomeGoalsFor += selfGoals;
                    HomeGoalsAgainst += oppositionGoals;
                    break;

                case LeagueMatchLocation.Away:
                    selfGoals = awayScore;
                    oppositionGoals = homeScore;

                    AwayGoalsFor += selfGoals;
                    AwayGoalsAgainst += oppositionGoals;
                    break;
            }
        }

        private void addResultStats(LeagueMatchResult result)
        {
            switch (result)
            {
                case LeagueMatchResult.HomeWin:
                    HomeWins++;
                    break;
                case LeagueMatchResult.HomeDraw:
                    HomeDraws++;
                    break;
                case LeagueMatchResult.HomeLoss:
                    HomeLosses++;
                    break;
                case LeagueMatchResult.AwayWin:
                    AwayWins++;
                    break;
                case LeagueMatchResult.AwayDraw:
                    AwayDraws++;
                    break;
                case LeagueMatchResult.AwayLoss:
                    AwayLosses++;
                    break;
            }
        }     

        private LeagueMatchResult determineResult(int selfGoals, int oppositionGoals, LeagueMatchLocation location)
        {
            if (selfGoals > oppositionGoals)
                return location == LeagueMatchLocation.Home ? LeagueMatchResult.HomeWin : LeagueMatchResult.AwayWin;
            else if (selfGoals == oppositionGoals)
                return location == LeagueMatchLocation.Home ? LeagueMatchResult.HomeDraw : LeagueMatchResult.AwayDraw;
            else
                return location == LeagueMatchLocation.Home ? LeagueMatchResult.HomeLoss : LeagueMatchResult.AwayLoss;
        }

        private LeagueMatchLocation getMatchLocation(HattrickXmlReader reader)
        {
            int homeTeamId = reader.ReadNumber("HomeTeamID");
            if (homeTeamId == TeamID)
                return LeagueMatchLocation.Home;
            else
                return LeagueMatchLocation.Away;
        }

        public override string ToString()
        {
            return string.Format("{0}/8 with {1} points after {2} matches", Position, Points, PlayedMatches);
        }

        private enum LeagueMatchResult
        {
            HomeWin,
            HomeDraw,
            HomeLoss,
            AwayWin,
            AwayDraw,
            AwayLoss
        }

        private enum LeagueMatchLocation
        {
            Home,
            Away
        }
    }
}